package com.bzkj.document.utils.otherinbound;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.bzkj.common.domain.NccApiResponse;
import com.bzkj.document.domain.otherinorder.dto.ICGeneralInB;
import com.bzkj.document.enums.ProcessingType;
import com.bzkj.document.exception.WarehouseNotFoundException;
import com.bzkj.document.mapper.OtherInboundOrderMapper;
import com.bzkj.document.mapper.WmsWareHouseInventoryMapper;
import com.bzkj.document.utils.common.DataProcessUtils;
import com.bzkj.document.utils.common.InventoryManagerUtils;
import org.apache.commons.lang3.function.TriFunction;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @projectName: prt-wms
 * @package: com.bzkj.document.utils.otherinbound
 * @className: OtherInboundUtils
 * @description: 其他入库单工具类
 * @author: C1309
 * @createDate: 2025年01月05日
 * @version: v1.0
 */
@Component
public class OtherInboundUtils {

    @Resource
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor executor;

    @Resource
    private OtherInboundOrderMapper otherInboundOrderMapper;

    @Resource
    private InventoryManagerUtils inventoryManagerUtils;

    @Resource
    private WmsWareHouseInventoryMapper wareHouseInventoryMapper;

    // ==========================================================
    // 模块名称: 其他入库单解析模块
    // 功能描述: 解析其他入库单相关的业务逻辑
    // ==========================================================

    /**
     * 解析NCC材料出库单
     */
    public List<Map<String, Object>> parsingOtherInboundOrders(NccApiResponse nccApiResponse, int type, Boolean isConfirm) {
        return parseOrdersWithThreadPool(nccApiResponse, isConfirm, (bodyList, vbillcode, generalInHeadMap) ->
                parseGeneralInBodyList(bodyList, vbillcode, generalInHeadMap, type));
    }

    /**
     * 解析NCC材料出库单(针对新增)
     */
    public List<Map<String, Object>> parsingOtherInboundOrders(NccApiResponse nccApiResponse, List<ICGeneralInB> icGeneralInList, Boolean isConfirm) {
        return parseOrdersWithThreadPool(nccApiResponse, isConfirm, (bodyList, vbillcode, generalInHeadMap) ->
                parseGeneralInBodyList(bodyList, vbillcode, generalInHeadMap, icGeneralInList));
    }

    /**
     * 使用线程池解析订单
     */
    private List<Map<String, Object>> parseOrdersWithThreadPool(NccApiResponse nccApiResponse, Boolean isConfirm,
                                                                TriFunction<JSONArray, String, Map<String, Object>, List<Map<String, Object>>> bodyProcessor) {
        JSONArray dataArray = Optional.ofNullable(nccApiResponse.getData()).orElse(new JSONArray());

        List<CompletableFuture<Map<String, Object>>> futures = dataArray.stream()
                .map(obj -> obj instanceof Map ? new JSONObject((Map<String, Object>) obj) : (JSONObject) obj)
                .map(dataObject -> CompletableFuture.supplyAsync(() -> processOrder(dataObject, isConfirm, bodyProcessor), executor))
                .collect(Collectors.toList());

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

        try {
            allFutures.get(); // 等待所有线程完成
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            throw new RuntimeException("处理订单时发生异常:" + e.getMessage(), e);
        }

        return futures.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private Map<String, Object> processOrder(JSONObject dataObject, Boolean isConfirm,
                                             TriFunction<JSONArray, String, Map<String, Object>, List<Map<String, Object>>> bodyProcessor) {
        JSONObject generalInHead = dataObject.getJSONObject("ic.GeneralInHeadVO");
        if (generalInHead == null) {
            return null;
        }

        Map<String, Object> generalInHeadMap = parseGeneralInHead(generalInHead);
        String vbillcode = generalInHeadMap.get("vbillcode").toString();

        // 过滤掉4A-02类型的单据和已经存在的单据
        if ("4A-02".equals(generalInHeadMap.get("vtrantypecode")) ||
                (otherInboundOrderMapper.selectIsExist(vbillcode) > 0 && isConfirm)) {
            return null;
        }

        generalInHeadMap.put("isConfirm", 0);
        JSONArray generalInBodyList = dataObject.getJSONArray("ic.GeneralInBodyVO");
        if (generalInBodyList == null || generalInBodyList.isEmpty()) {
            return null;
        }

        List<Map<String, Object>> details = bodyProcessor.apply(generalInBodyList, vbillcode, generalInHeadMap);
        Map<String, Object> billData = new HashMap<>(2);
        billData.put("header", generalInHeadMap);
        billData.put("details", details);
        return billData;
    }

    /**
     * 解析订单头数据
     *
     * @param header 订单头 JSON 对象
     * @return 订单头解析结果
     */
    private Map<String, Object> parseGeneralInHead(JSONObject header) {
        // 解析订单头逻辑
        return parsingGeneralInHeadVO(header);
    }

    /**
     * 解析订单行数据列表
     *
     * @param generalInBodyList 订单行 JSON 数组
     * @param vbillcode         订单编号
     * @param generalInHeadMap  订单头数据
     * @param type              处理类型
     * @return 订单行解析结果列表
     */
    private List<Map<String, Object>> parseGeneralInBodyList(JSONArray generalInBodyList, String vbillcode, Map<String, Object> generalInHeadMap, int type) {
        List<Map<String, Object>> result;
        List<Exception> exceptions = new ArrayList<>(); // 用于收集异常
        result = generalInBodyList.stream()
                .map(obj -> JSONObject.parseObject(JSONObject.toJSONString(obj)))
                .map(otherInBody -> {
                    Map<String, Object> bodyMap = parsingOtherInBodyVOList(otherInBody);
                    Object cwarehouseid = generalInHeadMap.get("cwarehouseid");
                    bodyMap.put("vbillcode", vbillcode);
                    bodyMap.put("cwarehouseid", cwarehouseid);
                    bodyMap.put("cgeneralhid", generalInHeadMap.get("cgeneralhid").toString());
                    bodyMap.put("vtrantypecode", generalInHeadMap.get("vtrantypecode").toString());
                    if (type == ProcessingType.CONFIRM_ORDER.ordinal()) {
                        try {
                            processWarehouseInfo(bodyMap, cwarehouseid);
                        } catch (Exception e) {
                            exceptions.add(e);
                        }
                    }
                    return bodyMap;
                })
                .collect(Collectors.toList());
        // 流处理完成后，统一处理异常
        if (!exceptions.isEmpty()) {
            // 可以记录日志、抛出聚合异常，或进行其他处理
            StringBuilder errorMessage = new StringBuilder();
            for (Exception exception : exceptions) {
                if (errorMessage.toString().isEmpty()) {
                    errorMessage.append("<ul>");
                }
                errorMessage.append(exception.getMessage());
            }
            throw new RuntimeException(errorMessage.append("</ul>").toString());
        }
        return result;
    }

    /***
     * 处理仓库信息
     * @param bodyMap 订单行数据
     * @param cwarehouseid NCC仓库信息
     */
    private void processWarehouseInfo(Map<String, Object> bodyMap, Object cwarehouseid) {
        String pkOrCode = DataProcessUtils.getPkOrCode(bodyMap, "cmaterialoid");
        @SuppressWarnings("unchecked")
        Map<String, Object> cmaterialoid = (Map<String, Object>) bodyMap.get("cmaterialoid");
        Object crowno = bodyMap.get("crowno");
        String warehousePk = DataProcessUtils.getPkOrCode((Map<String, Object>) cwarehouseid);
        List<HashMap<String, String>> warehouseList = inventoryManagerUtils.selectNccWarehouse(pkOrCode, warehousePk);
        if (warehouseList != null && !warehouseList.isEmpty()) {
            HashMap<String, String> hashMap = warehouseList.get(0);
            bodyMap.put("sto_id", hashMap.get("sto_id"));
            bodyMap.put("stu_class_id", hashMap.get("stu_class_id"));
        } else {
            String errorMsg = "<li>行号->" + crowno + "：物料名称：<b>" + cmaterialoid.get("name") +
                    "<b>" + "在WMS系统中无法分配对应仓库！</li>";
            throw new WarehouseNotFoundException(errorMsg);
        }
    }

    /**
     * 解析订单行数据列表
     *
     * @param generalInBodyList 订单行 JSON 数组
     * @param vbillcode         单据号
     * @param generalInHeadMap  表头
     * @param icGeneralInList   订单行数据
     * @return 订单行解析结果列表
     */
    private List<Map<String, Object>> parseGeneralInBodyList(JSONArray generalInBodyList, String vbillcode, Map<String, Object> generalInHeadMap, List<ICGeneralInB> icGeneralInList) {
        List<Map<String, Object>> details = new ArrayList<>();
        for (int j = 0; j < generalInBodyList.size(); j++) {
            JSONObject otherInBody = generalInBodyList.getJSONObject(j);
            Map<String, Object> bodyMap = parsingOtherInBodyVOList(otherInBody);
            String pkOrCode = DataProcessUtils.getPkOrCode(bodyMap, "cmaterialvid");
            for (ICGeneralInB icGeneralInB : icGeneralInList) {
                if (pkOrCode.equals(icGeneralInB.getcMaterialVid())) {
                    bodyMap.put("sto_id", icGeneralInB.getStoId());
                    bodyMap.put("stu_class_id", icGeneralInB.getStuClassId());
                }
            }
            bodyMap.put("vbillcode", vbillcode);
            bodyMap.put("cwarehouseid", generalInHeadMap.get("cwarehouseid"));
            bodyMap.put("cgeneralhid", generalInHeadMap.get("cgeneralhid").toString());
            bodyMap.put("vtrantypecode", generalInHeadMap.get("vtrantypecode").toString());
            details.add(bodyMap);
        }
        return details;
    }

    /**
     * 解析NCC其他入库单头数据
     *
     * @param finProdInHeadVO 其他入库单头数据
     * @return 解析结果
     */
    private HashMap<String, Object> parsingGeneralInHeadVO(JSONObject finProdInHeadVO) {

        HashMap<String, Object> finProdInHead = new HashMap<>(18);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // modifier(最后修改人)
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "modifier");

        // fbillflag(单据状态)
        DataProcessUtils.getStringField(finProdInHeadVO, "fbillflag", finProdInHead);

        // ntotalnum（总数量）
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "ntotalnum", finProdInHead);

        //cgeneralhid（其他入库单表头主键）
        DataProcessUtils.getStringField(finProdInHeadVO, "cgeneralhid", finProdInHead);

        //ctrantypeid(出入库类型）
        DataProcessUtils.getStringField(finProdInHeadVO, "ctrantypeid", finProdInHead);

        //dmakedate(制单日期）
        DataProcessUtils.handleDateField(finProdInHeadVO, "dmakedate", sdf, finProdInHead);

        //billmaker（制单人）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "billmaker");

        // creationtime（创建时间）
        DataProcessUtils.handleDateField(finProdInHeadVO, "creationtime", sdf, finProdInHead);

        // pk_org_v（库存组织）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "pk_org_v");

        //modifiedtime（最后修改时间）
        DataProcessUtils.handleDateField(finProdInHeadVO, "modifiedtime", sdf, finProdInHead);

        //creator（创建人）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "creator");

        //cwarehouseid（仓库）
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cwarehouseid");

        //vtrantypecode（出库类型编码）
        DataProcessUtils.getStringField(finProdInHeadVO, "vtrantypecode", finProdInHead);

        //vbillcode（单据编号）
        DataProcessUtils.getStringField(finProdInHeadVO, "vbillcode", finProdInHead);

        // vnote(备注）
        DataProcessUtils.getStringField(finProdInHeadVO, "vnote", finProdInHead);

        //dbilldate（单据日期）
        DataProcessUtils.handleDateField(finProdInHeadVO, "dbilldate", sdf, finProdInHead);

        //corpoid
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "corpoid");

        //cdptvid
        DataProcessUtils.processField(finProdInHeadVO, finProdInHead, "cdptvid");

        //ntotalvolume
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "ntotalvolume", finProdInHead);

        //ntotalpiece
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "ntotalpiece", finProdInHead);

        //ntotalweight
        DataProcessUtils.getFormattedDouble(finProdInHeadVO, "ntotalweight", finProdInHead);
        return finProdInHead;
    }

    /**
     * 解析NCC其他入库单行数据
     *
     * @param generalInBodyVO 其他入库单行数据
     * @return 解析结果
     */
    private Map<String, Object> parsingOtherInBodyVOList(JSONObject generalInBodyVO) {
        HashMap<String, Object> generalInBody = new HashMap<>(20);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // dproducedate(生产日期）
        DataProcessUtils.handleDateField(generalInBodyVO, "dproducedate", sdf, generalInBody);

        //cbodytranstypecode（入库类型编码）
        DataProcessUtils.getStringField(generalInBodyVO, "cbodytranstypecode", generalInBody);

        // nshouldassistnum(应收数量）
        DataProcessUtils.getFormattedDouble(generalInBodyVO, "nshouldassistnum", generalInBody);

        // dbizdate（入库日期）
        DataProcessUtils.handleDateField(generalInBodyVO, "dbizdate", sdf, generalInBody);

        // bhasiabill(是否已传存货核算)
        DataProcessUtils.getStringField(generalInBodyVO, "bhasiabill", generalInBody);

        //cgeneralhid(表头主键）
        DataProcessUtils.getStringField(generalInBodyVO, "cgeneralhid", generalInBody);

        // cgeneralbid（表体主键）
        DataProcessUtils.getStringField(generalInBodyVO, "cgeneralbid", generalInBody);

        //cstateid（库存状态）
        DataProcessUtils.getStringField(generalInBodyVO, "cstateid", generalInBody);

        //pk_batchcode（批次主键）
        DataProcessUtils.getStringField(generalInBodyVO, "pk_batchcode", generalInBody);

        // nshouldnum（应收主数量）
        DataProcessUtils.getFormattedDouble(generalInBodyVO, "nshouldnum", generalInBody);

        // castunitid（单位）
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "castunitid");

        //cunitid（主单位）
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cunitid");

        //vapprovalnumber_148(批准文号）
        DataProcessUtils.getStringField(generalInBodyVO, "vapprovalnumber_148", generalInBody);

        //dvalidate（失效日期）
        DataProcessUtils.handleDateField(generalInBodyVO, "dvalidate", sdf, generalInBody);

        //cmaterialvid（物料编码）
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cmaterialvid");

        @SuppressWarnings("unchecked")
        HashMap<String, Object> pkMaterial = (HashMap<String, Object>) generalInBody.get("cmaterialvid");

        // 查询物料的其他信息（保质期管理、批次管理、保质期单位、保质期）
        String pk = pkMaterial.get("pk").toString();
        HashMap<String, Object> additionMap = wareHouseInventoryMapper.selectAdditionalManagement(pk);
        generalInBody.put("additionMap", additionMap);

        //vchangerate(换算率）
        DataProcessUtils.getStringField(generalInBodyVO, "vchangerate", generalInBody);

        //crowno（行号）
        DataProcessUtils.getStringField(generalInBodyVO, "crowno", generalInBody);

        // cbodywarehouseid（库存仓库）
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cbodywarehouseid");

        // nnum（应收主数量）
        DataProcessUtils.getFormattedDouble(generalInBodyVO, "nnum", generalInBody);

        //cproductorid
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cproductorid");

        //nassistnum
        DataProcessUtils.getFormattedDouble(generalInBodyVO, "nassistnum", generalInBody);

        //cvendorid
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cvendorid");

        //cmaterialoid
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "cmaterialoid");

        //vbatchcode
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "vbatchcode");

        // pk_holder_148（注册人/持有人）
        DataProcessUtils.processField(generalInBodyVO, generalInBody, "pk_holder_148");

        // vvendbatchcode（供应商批次）
        DataProcessUtils.getStringField(generalInBodyVO, "vvendbatchcode", generalInBody);

        // vbcdef2（供应商代码）
        DataProcessUtils.getStringField(generalInBodyVO, "vbcdef2", generalInBody);

        // vbdef48（复验期）
        DataProcessUtils.getStringField(generalInBodyVO, "vbdef48", generalInBody);
        return generalInBody;
    }
}
